package com.company.finance.ddd.domain.core.bill.aggregate;

import com.company.finance.ddd.common.FlagEnum;
import com.company.finance.ddd.common.VoucherCreateException;
import com.company.finance.ddd.domain.core.voucher.aggregate.Voucher;
import com.company.finance.ddd.domain.core.voucher.VoucherService;
import com.company.finance.ddd.domain.core.voucher.rule.RuleHolder;
import com.company.finance.ddd.domain.core.voucher.rule.RuleConfigurationLoader;
import com.company.finance.ddd.domain.core.voucher.rule.RuleDefinition;
import com.company.finance.ddd.domain.core.bill.enums.BillSubTypeEnum;
import com.company.finance.ddd.domain.core.voucher.rule.VoucherType;
import com.company.finance.ddd.domain.support.company.repository.CompanyRepository;
import com.company.finance.ddd.domain.support.supplier.repository.SupplierRepository;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * @author wanglei
 */
@Slf4j
@Builder
public class BillAggregate {

    private final Bill bill;
    private VoucherService voucherService;

    public BillAggregate voucherService(VoucherService voucherService) {
        this.voucherService = voucherService;
        return this;
    }

    public Bill getBill() {
        return bill;
    }

    public List<?> getItems() {
        return Items.builder().bill(bill).voucherService(voucherService).build().getItems();
    }

    public List<Voucher> createVoucherInstance() throws VoucherCreateException {
        List<Voucher> voucherList = new ArrayList<>();
        try {
            getVoucherCreatingRules().forEach(rule -> {
                log.info("Rule -> {}", rule);
                voucherList.add(buildVoucher(rule).items(buildVoucherItems(rule)));
            });
        } catch (Exception e) {
            throw new VoucherCreateException(e);
        }
        assert !voucherList.isEmpty();
        return voucherList;
    }

    private List<RuleHolder> getVoucherCreatingRules() {
        FlagEnum relateDeal = FlagEnum.getFlag(voucherService.getApplicationContext()
                .getBean(CompanyRepository.class).get(bill.getCOMPANY_ID()).getRELATE_DEAL());
        BillSubTypeEnum subTypeEnum = BillSubTypeEnum.getBillSubTypeEnum(bill.getSUB_TYPE_CODE());
        FlagEnum prepayment = FlagEnum.getFlag(bill.getPREPAYMENT());

        Set<VoucherType> voucherTypeSet = RuleConfigurationLoader.getVoucherTypes(bill.getSUB_TYPE_CODE());

        List<RuleHolder> ruleList = new ArrayList<>(voucherTypeSet.size());
        voucherTypeSet.forEach(voucherType ->
            ruleList.add(RuleConfigurationLoader
                    .getRule(RuleDefinition.builder()
                        .voucherType(voucherType)
                        .addBillSpecification(RuleDefinition.BillSpecification.builder()
                                    .billSubTypeEnum(subTypeEnum)
                                    .prepayment(prepayment)
                                    .relateDeal(relateDeal)
                                    .build())
                    .build())));
        return ruleList;
    }

    private Voucher buildVoucher(RuleHolder rule) {
        Date date = new Date();
        String[] bizDate = bill.getBIZ_DATE().toString().split("-");
        return Voucher.builder()
                .BILL_ID(bill.getID())
                .CREATE_TIME(date)
                .UPDATE_TIME(date)
                .PERIOD_CODE(bizDate[1])
                .PERIOD_YEAR(bizDate[0])
                .VOUCHER_TYPE(rule.getRuleDefinition().getVoucherType().getCode())
                .VOUCHER_TYPE_NAME(rule.getRuleDefinition().getVoucherType().getName())
                .build();
    }

    private List<Voucher.VoucherItem> buildVoucherItems(RuleHolder ruleHolder) {
        List<Voucher.VoucherItem> itemEntityList = new ArrayList<>();
        Items.builder().bill(bill).voucherService(voucherService).build()
                .getItems().forEach(billItem -> itemEntityList.addAll(
                        ItemAssemblerDispatcher.builder().billAggregate(this)
                                .billItemRule(ruleHolder.getRuleDefinition()
                                        .getMatchedBillSpecification(bill).getBillItemRule())
                                .build()
                                .dispatch()));
        return itemEntityList;
    }

    public String getSupplierName() {
        return voucherService.getApplicationContext()
                .getBean(SupplierRepository.class).get(bill.getSUPPLIER_ID()).getSUPPLIER_NAME();
    }
}
